﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.OMM.HealthMonitor.Entity;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.Receiver;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.Router;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.HftProcessor;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.Dumper;
using System.Messaging;
using Sinacor.OMM.HealthMonitor.Collector;
using System.Threading;
using Sinacor.OMM.HealthMonitor.Client;
using System.IO;
using Sinacor.OMM.HealthMonitor.Configuration;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.OrderExecutor;
using Sinacor.OMM.Util;
using PSE.Framework.Threading;

namespace Sinacor.OMM.HealthMonitor.Server.Controller
{
    public class HealthStatusController : IDisposable
    {
        private const string TRACE_FOLDER_NAME = "Monitor Sistema";

        private const int TIME_TO_SEND_HEALTH_STATUS = 1000;

        private HealthData _healthStatusMemorySnapshot;
        private TraceWriter _traceWriter;

        public delegate void HealthStatusUpdateHandler(byte[] healthStatusMemorySnapshotSerialized);
        public event HealthStatusUpdateHandler OnHealthStatusUpdate;

        private bool _keepRunning;

        public HealthStatusController()
        {
            _traceWriter = Tracer.GetInstance(TRACE_FOLDER_NAME, "Controle de Expiração de Status");
            _healthStatusMemorySnapshot = new HealthData();

            Thread thr = new Thread(new ThreadStart(HealthStatusMonitor));
            thr.Name = "HealthStatusController";
            thr.Start();
        }

        public void UpdateMemorySnapshot(Message healthMsg)
        {
            HealthType healthType;

            healthType = DefinteTipoMsg(healthMsg.Label);

            switch (healthType)
            {
                case HealthType.Receiver:
                    ReceiverShdms _receiverHealthStatus = (ReceiverShdms)healthMsg.Body;
                    lock (_healthStatusMemorySnapshot)
                    {
                        _healthStatusMemorySnapshot.ReceiverHealthStatus = _receiverHealthStatus.ReceiverDataInfo;
                        _healthStatusMemorySnapshot.ReceiverHealthStatus.LastUpdate = DateTime.Now;
                    }
                    _traceWriter.Trace("ReceiverShdms - Fez update dos dados em memória", TraceType.Optional);
                    break;

                case HealthType.Router:
                    RouterShdms _routerHealthStatus = (RouterShdms)healthMsg.Body;
                    lock (_healthStatusMemorySnapshot)
                    {
                        _healthStatusMemorySnapshot.RouterHealthStatus = _routerHealthStatus.RouterDataInfo;
                        _healthStatusMemorySnapshot.RouterHealthStatus.LastUpdate = DateTime.Now;
                    }
                    _traceWriter.Trace("RouterShdms - Fez update dos dados em memória", TraceType.Optional);
                    break;

                case HealthType.HftProcessor:
                    HftProcessorShdms _hftProcessorHealthStatus = (HftProcessorShdms)healthMsg.Body;
                    lock (_healthStatusMemorySnapshot)
                    {
                        lock (_healthStatusMemorySnapshot.HftProcessorHealthStatus)
                        {
                            if (_healthStatusMemorySnapshot.HftProcessorHealthStatus.ContainsKey(_hftProcessorHealthStatus.HFTProcessorDataInfo.HftId))
                            {
                                _healthStatusMemorySnapshot.HftProcessorHealthStatus[_hftProcessorHealthStatus.HftProcessId] = _hftProcessorHealthStatus.HFTProcessorDataInfo;
                                _healthStatusMemorySnapshot.HftProcessorHealthStatus[_hftProcessorHealthStatus.HftProcessId].LastUpdate = DateTime.Now;
                            }
                            else
                            {
                                _healthStatusMemorySnapshot.HftProcessorHealthStatus.Add(_hftProcessorHealthStatus.HFTProcessorDataInfo.HftId, _hftProcessorHealthStatus.HFTProcessorDataInfo);
                            }
                        }
                        _traceWriter.Trace("HftProcessorShdms - Fez update dos dados em memória (" + _hftProcessorHealthStatus.HftProcessId + ")", TraceType.Optional);
                    }
                    break;

                case HealthType.Dumper:
                    DumperShdms _dumperHealthStatus = (DumperShdms)healthMsg.Body;
                    lock (_healthStatusMemorySnapshot)
                    {
                        _healthStatusMemorySnapshot.DumperHealthStatus = _dumperHealthStatus.DumperDataInfo;
                        _healthStatusMemorySnapshot.DumperHealthStatus.LastUpdate = DateTime.Now;
                    }
                    _traceWriter.Trace("DumperShdms - Fez update dos dados em memória", TraceType.Optional);
                    break;

                case HealthType.Executor:
                    OrderExecutorShdms _orderExecutorStatus = (OrderExecutorShdms)healthMsg.Body;
                    lock (_healthStatusMemorySnapshot)
                    {
                        _healthStatusMemorySnapshot.OrderExecutorStatus = _orderExecutorStatus.OrderExecutorDataInfo;
                        _healthStatusMemorySnapshot.OrderExecutorStatus.LastUpdate = DateTime.Now;
                    }
                    _traceWriter.Trace("OrderExecutorShdms - Fez update dos dados em memória", TraceType.Optional);
                    break;
            }
        }


        private void HealthStatusMonitor()
        {
            _traceWriter.Trace("Thread HealthStatusMonitor started...");

            _keepRunning = true;
            while (_keepRunning)
            {

                ExpireOldedHealthStatus();
                _traceWriter.Trace("Terminou a verificação de expiração do status", TraceType.Optional);

                SendHealthStatusUpdate();
                _traceWriter.Trace("Enviou o BroadCast das informações", TraceType.Optional);

                Sleeper.Sleep(TIME_TO_SEND_HEALTH_STATUS, IsRunning);
            }

            _traceWriter.Trace("Thread HealthStatusMonitor stoped...");
        }


        private void ExpireOldedHealthStatus()
        {
            lock (_healthStatusMemorySnapshot)
            {
                //Avalia Receiver
                _healthStatusMemorySnapshot.ReceiverHealthStatus = HsCtrlReceiverShdms.CheckExpiration(_healthStatusMemorySnapshot.ReceiverHealthStatus);

                if (_healthStatusMemorySnapshot.ReceiverHealthStatus == null)
                    _traceWriter.Trace("Receiver HealthStatus foi EXPIRADO!", TraceType.Optional);


                //Avalia Router
                _healthStatusMemorySnapshot.RouterHealthStatus = HsCtrlRouterShdms.CheckExpiration(_healthStatusMemorySnapshot.RouterHealthStatus);

                if (_healthStatusMemorySnapshot.RouterHealthStatus == null)
                    _traceWriter.Trace("Router HealthStatus foi EXPIRADO!", TraceType.Optional);


                //Avalia Hft Processes
                int lastCount;

                lock (_healthStatusMemorySnapshot.HftProcessorHealthStatus)
                {
                    lastCount = _healthStatusMemorySnapshot.HftProcessorHealthStatus.Count;
                }
                _healthStatusMemorySnapshot.HftProcessorHealthStatus = HsCtrlHftProcessesShdms.CheckExpiration(_healthStatusMemorySnapshot.HftProcessorHealthStatus);

                lock (_healthStatusMemorySnapshot.HftProcessorHealthStatus)
                {
                    if (_healthStatusMemorySnapshot.HftProcessorHealthStatus.Count < lastCount)
                        _traceWriter.Trace("HFT Processor HealthStatus foi EXPIRADO!", TraceType.Optional);
                }


                //Avalia Dumper
                _healthStatusMemorySnapshot.DumperHealthStatus = HsCtrlHftDumperShdms.CheckExpiration(_healthStatusMemorySnapshot.DumperHealthStatus);
                
                if (_healthStatusMemorySnapshot.DumperHealthStatus == null)
                    _traceWriter.Trace("Dumper HealthStatus foi EXPIRADO!", TraceType.Optional);


                //Avalia Order Executor
                _healthStatusMemorySnapshot.OrderExecutorStatus = HsCtrlOrderExecutorShdms.CheckExpiration(_healthStatusMemorySnapshot.OrderExecutorStatus);
                
                if (_healthStatusMemorySnapshot.OrderExecutorStatus == null)
                    _traceWriter.Trace("Executor HealthStatus foi EXPIRADO!", TraceType.Optional);
            }
        }

        private HealthType DefinteTipoMsg(string intMessageType)
        {
            HealthType ret;

            int type = int.Parse(intMessageType);

            switch (type)
            {
                case (int)HealthType.Receiver:
                    ret = HealthType.Receiver;
                    break;
                case (int)HealthType.Router:
                    ret = HealthType.Router;
                    break;
                case (int)HealthType.HftProcessor:
                    ret = HealthType.HftProcessor;
                    break;
                case (int)HealthType.Dumper:
                    ret = HealthType.Dumper;
                    break;
                case (int)HealthType.Executor:
                    ret = HealthType.Executor;
                    break;
                default:
                    ret = HealthType.Undefined;
                    break;
            }

            return ret;
        }


        private void SendHealthStatusUpdate()
        {
            try
            {
                string messageType;
                int intmessageSize;
                string messageSize = string.Empty;

                byte[] buffer;
                byte[] messageSerilized;

                lock (_healthStatusMemorySnapshot)
                    messageSerilized = HealthData.Serialize(_healthStatusMemorySnapshot);

                messageType = "01"; //Tipo de mensage 00 = HeartBeat    -    01 = HealthStatus

                intmessageSize = messageSerilized.Length.ToString().Length;

                messageSize = messageSize.PadLeft(HealthMonitorMessage.HEADERSIZE - intmessageSize,'0');
                //messageSize = (new StringBuilder("")).Append('0', HealthMonitorMessage.HEADERSIZE - intmessageSize).ToString();

                messageSize = messageSize + messageSerilized.Length.ToString();

                buffer = new byte[HealthMonitorMessage.HEADERSIZE + HealthMonitorMessage.MESSAGETYPESIZE + messageSerilized.Length];

                MemoryStream ms = new MemoryStream();
                ms.Write(Encoding.Default.GetBytes(messageSize), 0, messageSize.Length);
                ms.Write(Encoding.Default.GetBytes(messageType), 0, messageType.Length);
                ms.Write(messageSerilized, 0, messageSerilized.Length);
                ms.Flush();
                ms.Position = 0;
                ms.Read(buffer, 0, buffer.Length);
                ms.Flush();
                ms.Close();
                ms.Dispose();
                ms = null;

                try
                {
                    if (OnHealthStatusUpdate != null)
                        OnHealthStatusUpdate(buffer);
                }
                catch (Exception ex)
                {
                    _traceWriter.Trace("Erro ao fazer OnHealthStatusUpdate()");
                }

            }
            catch (Exception ex)
            {
                _traceWriter.Trace("Erro SendHealthStatusUpdate...");
                _traceWriter.Trace("SendHealthStatusUpdate: [" + ex.StackTrace);
            }
        }

        private bool IsRunning()
        {
            return _keepRunning;
        }

        #region IDisposable Members


        public void Dispose()
        {
            _keepRunning = false;
        }

        #endregion
    }
}
